<?php

namespace app\common\model;

use ba\Random;
use think\Model;
use think\facade\Db;
use think\Exception;

/**
 * 兑换码模型
 */
class RedemptionCode extends Model
{
    protected $name = 'redemption_code';
    // 自动写入时间戳字段
    protected $autoWriteTimestamp = true;

    // 状态常量
    const STATUS_DISABLED = '0';  // 禁用
    const STATUS_ACTIVE = '1';    // 启用
    const STATUS_EXPIRED = '2';   // 已过期

    // 类型常量
    const TYPE_SINGLE = 'single';     // 单次使用
    const TYPE_MULTIPLE = 'multiple'; // 多次使用

    const DEFAULT_CODE_LENGTH = 7; // 默认兑换码长度(不包含前缀)


    /**
     * 生成唯一兑换码
     * @param string $prefix 前缀，默认为'JYB'
     * @param int $totalLength 总长度，默认为8
     * @return string
     */
    public static function generateUniqueCode(string $prefix = 'JYB', int $totalLength = 12): string
    {
        // 计算随机部分的长度
        // $randomLength = $totalLength - strlen($prefix);

        //
        // if ($randomLength < self::DEFAULT_CODE_LENGTH) {
        //     $randomLength = self::DEFAULT_CODE_LENGTH;
        //     $totalLength = strlen($prefix) + $randomLength;
        // }

        do {
            // 计算随机码长度（总长度减去前缀长度）
            $randomLength = $totalLength - strlen($prefix);
            // 确保至少有默认长度位随机字符
            if ($randomLength < self::DEFAULT_CODE_LENGTH) {
                $randomLength = self::DEFAULT_CODE_LENGTH; // 最少默认长度位随机码
            }

            // 生成随机码
            $randomCode = Random::build('alnum', $randomLength, true);
            $randomCode = str_replace(['0', 'O', '1', 'I'], ['2', '3', '4', '5'], $randomCode);

            // 组合前缀和随机码
            $code = $prefix . $randomCode;


            // 检查是否已存在
            $exists = self::where('code', $code)->find();
        } while ($exists);

        return $code;
    }


    /**
     * 检查是否过期
     * @return bool
     */
    public function isExpired(): bool
    {
        return ($this->start_time && $this->start_time > time()) || ($this->end_time && $this->end_time < time());
    }

    /**
     * 检查是否可用
     * @return bool
     */
    public function isAvailable(): bool
    {
        return $this->status === self::STATUS_ACTIVE
            && !$this->isExpired()
            && $this->used_count < $this->max_uses;
    }

    /**
     * 使用兑换码
     * @param int $userId 用户ID
     * @param int $orderId 订单ID（可选）
     * @return bool
     * @throws Exception
     */
    public function use(int $userId, int $orderId = 0, int $resultId = 0): bool
    {
        if (!$this->isAvailable()) {
            throw new Exception('兑换码不可用');
        }

        if ($this->isExpired()) {
            throw new Exception('兑换码未在有效期内');
        }

        // 检查是否达到最大使用次数
        if ($this->used_count >= $this->max_uses) {
            throw new Exception('兑换码使用次数已达上限');
        }

        // 开启事务
        Db::startTrans();
        try {
            // 增加使用次数
            $this->used_count += 1;

            // 如果达到最大使用次数，设置为禁用状态
            if ($this->used_count >= $this->max_uses) {
                $this->status = self::STATUS_DISABLED;
            }

            $this->update_time = time();
            $this->save();

            // 记录使用记录
            RedemptionCodeUsage::create([
                'code_id' => $this->id,
                'code' => $this->code,
                'user_id' => $userId,
                'order_id' => $orderId,
                'result_id' => $resultId,
                'discount_amount' => 0,
                'original_amount' => 0, // 需要根据实际业务设置
                'final_amount' => 0, // 需要根据实际业务设置
                'ip_address' => request()->ip() ?? '',
                'user_agent' => request()->header('user-agent') ?? '',
                'status' => 'success',
                'create_time' => time(),
            ]);

            // 更新批次使用统计
            if (!empty($this->batch_id)) {
                $batch = RedemptionCodeBatch::where('id', $this->batch_id)->find();
                if ($batch) {
                    $batch->used_count += 1;
                    $batch->update_time = time();

                    // 如果批次中所有兑换码都已使用完，设置批次为已完成
                    if ($batch->used_count >= $batch->total_count) {
                        $batch->status = RedemptionCodeBatch::STATUS_COMPLETED;
                    }

                    $batch->save();
                }
            }

            Db::commit();
            return true;
        } catch (Exception $e) {
            Db::rollback();
            throw $e;
        }
    }

    /**
     * 关联使用记录
     * @return \think\model\relation\HasMany
     */
    public function usageRecords(): \think\model\relation\HasMany
    {
        return $this->hasMany(RedemptionCodeUsage::class, 'code_id', 'id');
    }

    /**
     * 关联批次
     * @return \think\model\relation\BelongsTo
     */
    public function batch(): \think\model\relation\BelongsTo
    {
        return $this->belongsTo(RedemptionCodeBatch::class, 'batch_id', 'id');
    }
}
